# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: xguo

from coin.exchange.base.kr_rest.private_client_base import PrivateClientBase
from coin.exchange.kraken_futures.kr_rest.futures_product import (
    KrakenFuturesProduct,
    KrakenCurrency,
)
from coin.proto.coin_query_pb2 import (
    AccountBalance,
    AccountPosition,
    CurrencyBalance,
    ProductPosition,
)
from coin.exchange.kraken_futures.kr_rest.native_private_client import (
    KrakenFuturesNativePrivateClient,)


class KrakenFuturesPrivateParser(object):
  @staticmethod
  def parse_wallet_balance(msg):
    """
    ...
    "cash": {
      "type": "cashAccount",
      "balances": {
        "xbt": 0.0,
        "xrp": 0.0,
        "eth": 5.0,
        "ltc": 0.0,
        "bch": 0.0
      }
    }
    """
    assert msg['result'] == 'success', msg
    cash_account = msg['accounts']['cash']
    assert cash_account['type'] == 'cashAccount', msg
    balance_list = []
    for native_currency, balance in cash_account['balances'].items():
      currency = KrakenCurrency.FromStrNativeCurrencyNothrow(native_currency)
      if currency is None:
        continue

      total = float(balance)
      hold = 0
      available = total - hold
      balance = CurrencyBalance(
          currency=currency.currency,
          currency_native=currency.native_currency,
          total=total,
          available=available,
          hold=hold,
      )
      balance_list.append(balance)
    return AccountBalance(
        exchange='Kraken',
        market_type='Futures',
        each_balance=balance_list,
    )

  @staticmethod
  def parse_rest_trade_balance(msg):
    """
    "fv_xrpxbt": {
      "type": "marginAccount",
      "auxiliary": {
        "usd": 0,
        "pv": 0.29998748581,
        "pnl": -3.73e-06,
        "af": 0.29997962781,
        "funding": -2.5356e-07
      },
      "marginRequirements": {
        "im": 7.858e-06,
        "mm": 3.929e-06,
        "lt": 3.929e-06,
        "tt": 3.929e-06
      },
      "triggerEstimates": {
        "im": 0,
        "mm": 0,
        "lt": 0,
        "tt": 0
      },
      "balances": {
        "xbt": 0.29999146937,
        "xrp": 0.0,
        "eth": 0.0,
        "ltc": 0.0,
        "bch": 0.0,
        "pv_xrpxbt": 2.0
      },
      "currency": "xbt"
    },
    """
    assert msg['result'] == 'success', msg
    balance_dict = {}
    for product, info in msg['accounts'].items():
      if info['type'] != 'marginAccount':
        continue

      native_currency = info['currency']
      currency = KrakenCurrency.FromStrNativeCurrencyNothrow(native_currency)
      if currency is None:
        continue
      total = info['balances'][native_currency]
      available = info['auxiliary']['af']
      hold = total - available
      balance = CurrencyBalance(
          currency=currency.currency,
          currency_native=currency.native_currency,
          total=total,
          available=available,
          hold=hold,
      )
      if native_currency in balance_dict:
        balance_dict[native_currency].total += balance.total
        balance_dict[native_currency].available += balance.available
        balance_dict[native_currency].hold += balance.hold
      else:
        balance_dict[native_currency] = balance
    return AccountBalance(
        exchange='Kraken',
        market_type='Futures',
        each_balance=balance_dict.values(),
    )

  @staticmethod
  def parse_unrealized_pnl(msg):
    assert msg['result'] == 'success', msg
    unrealized_pnl = {}
    for product, info in msg['accounts'].items():
      if info['type'] != 'marginAccount':
        continue

      native_currency = info['currency']
      currency = KrakenCurrency.FromStrNativeCurrencyNothrow(native_currency)
      if currency is None:
        continue
      unrealized_pnl[currency.currency] = info['auxiliary']['pnl']
    return unrealized_pnl

  @staticmethod
  def parse_rest_account_position(pos_msg):
    """
    {
      "result": "success",
      "openPositions": [
        {
          "side": "long",
          "symbol": "pv_xrpxbt",
          "price": 8.0445e-05,
          "fillTime": "2019-03-20T03:37:19.478Z",
          "size": 2
        }
      ],
      "serverTime": "2019-03-20T03:37:19.478Z"
    }
    """
    assert pos_msg['result'] == 'success', pos_msg
    position_list = []
    for msg in pos_msg['openPositions']:
      product = KrakenFuturesProduct.FromStrNativeProduct(msg['symbol'])
      if msg['side'] == 'long':
        long_position = float(msg['size'])
        long_avg_entry_price = float(msg['price'])
        product_position = ProductPosition(
            symbol=product.symbol,
            long_position=long_position,
            net_position=long_position,
            long_avg_entry_price=long_avg_entry_price,
            avg_entry_price=long_avg_entry_price,
        )
      elif msg['side'] == 'short':
        short_position = -float(msg['size'])
        short_avg_entry_price = float(msg['price'])
        product_position = ProductPosition(
            symbol=product.symbol,
            short_position=short_position,
            net_position=short_position,
            short_avg_entry_price=short_avg_entry_price,
            avg_entry_price=short_avg_entry_price,
        )
      else:
        raise ValueError('Invalid side %s', pos_msg)
      position_list.append(product_position)

    return AccountPosition(
        exchange='Kraken',
        market_type='Futures',
        each_position=position_list,
    )


class KrakenFuturesPrivateClient(PrivateClientBase):
  ProductType = KrakenFuturesProduct

  def __init__(self, key_file):
    super().__init__()
    self.nprivc = KrakenFuturesNativePrivateClient(key_file=key_file)

  def query_account_balance_impl(self):
    update = self.nprivc.query_accounts()
    update.msg = KrakenFuturesPrivateParser.parse_rest_trade_balance(update.msg)
    return update

  def query_account_position_impl(self):
    update = self.nprivc.query_open_position()
    update.msg = KrakenFuturesPrivateParser.parse_rest_account_position(update.msg)
    return update

  def query_list_orders_impl(self, product):
    raise NotImplementedError()

  def query_fills_impl(self, product):
    raise NotImplementedError()

  def query_submit_impl(self, product, side, price, qty):
    raise NotImplementedError()

  def query_cancel_impl(self, product, order_id):
    raise NotImplementedError()

  def query_cancel_product_impl(self, product):
    raise NotImplementedError()

  def query_account_fills_impl(self):
    raise NotImplementedError()

  def query_account_list_orders_impl(self):
    raise NotImplementedError()
